﻿using System.Diagnostics;
using RealidadeAumentada.Entidades;

namespace RealidadeAumentada.Servicos.Rastreador
{
    public class RastreadorEscalaCinza : Rastreador_Principios
    {
        private IdoFilterImpl _impl;
	    protected object _buf;
	    
	    protected bool _is_attached_buffer;

        public RastreadorEscalaCinza(int i_width, int i_height)
            : base(i_width, i_height, EBuffer.INT1D_GRAY_8)
	    {
		    if (!ObterInstancia(this._size, EBuffer.INT1D_GRAY_8, true))
		    {
			    throw new Excessao();
		    }
	    }
	    
        public RastreadorEscalaCinza(int i_width, int i_height, bool i_is_alloc)
            : base(i_width, i_height, EBuffer.INT1D_GRAY_8)
	    {
		    if (!ObterInstancia(this._size, EBuffer.INT1D_GRAY_8, i_is_alloc))
		    {
                throw new Excessao();
		    }
	    }

        public RastreadorEscalaCinza(int i_width, int i_height, int i_raster_type, bool i_is_alloc)
            : base(i_width, i_height, i_raster_type)
	    {
		    if (!ObterInstancia(this._size, i_raster_type, i_is_alloc)) {
                throw new Excessao();
		    }
	    }

	    protected bool ObterInstancia(ETamanho i_size, int i_buf_type,bool i_is_alloc)
	    {
		    switch (i_buf_type) {
		    case EBuffer.INT1D_GRAY_8:
			    this._impl=new IdoFilterImpl_INT1D_GRAY_8();
			    this._buf = i_is_alloc ? new int[i_size.w * i_size.h] : null;
			    break;
		    default:
			    return false;
		    }
		    this._is_attached_buffer = i_is_alloc;
		    return true;
	    }
	    public override object ObterBuffer()
	    {
		    return this._buf;
	    }

	    public override bool TemBuffer()
	    {
		    return this._buf != null;
	    }
	    
        public override void CobrirBuffer(object i_ref_buf)
	    {
		    Debug.Assert (!this._is_attached_buffer);// ƒoƒbƒtƒ@‚ªƒAƒ^ƒbƒ`‚³‚ê‚Ä‚¢‚½‚ç‹@”\‚µ‚È‚¢B
		    this._buf = i_ref_buf;
	    }

	    public void fill(int i_value)
	    {
		   Debug.Assert (this.EhIgualTipoBuffer(this.ObterTipoBuffer()));
		    this._impl.Preenche(this,i_value);
	    }

        public void copyTo(int i_left, int i_top, int i_skip, RastreadorEscalaCinza o_output)
	    {
		    Debug.Assert (this.ObterTamanho().EhTamanhoInterno(i_left + o_output.ObterLargura() * i_skip, i_top+ o_output.ObterAltura() * i_skip));		
		    Debug.Assert (this.EhIgualTipoBuffer(o_output.ObterTipoBuffer()));
		    this._impl.CopiarPara(this, i_left, i_top, i_skip, o_output);
		    return;
	    }























        interface IdoFilterImpl
        {
            void Preenche(RastreadorEscalaCinza i_raster, int i_value);
            void CopiarPara(RastreadorEscalaCinza i_input, int i_left, int i_top, int i_skip, RastreadorEscalaCinza o_output);
        }

        sealed class IdoFilterImpl_INT1D_GRAY_8 : IdoFilterImpl
        {
            public void Preenche(RastreadorEscalaCinza i_raster, int i_value)
            {
                Debug.Assert(i_raster._buffer_type == EBuffer.INT1D_GRAY_8);
                int[] buf = (int[])i_raster._buf;
                for (int i = i_raster._size.h * i_raster._size.w - 1; i >= 0; i--)
                {
                    buf[i] = i_value;
                }
            }

            public void CopiarPara(RastreadorEscalaCinza i_input, int i_left, int i_top, int i_skip, RastreadorEscalaCinza o_output)
            {
                Debug.Assert(i_input.ObterTamanho().EhTamanhoInterno(i_left + o_output.ObterLargura() * i_skip, i_top + o_output.ObterAltura() * i_skip));
                int[] input = (int[])i_input.ObterBuffer();
                int[] output = (int[])o_output.ObterBuffer();
                int pt_src, pt_dst;
                ETamanho dest_size = o_output.ObterTamanho();
                ETamanho src_size = i_input.ObterTamanho();
                int skip_src_y = (src_size.w - dest_size.w * i_skip) + src_size.w * (i_skip - 1);
                int pix_count = dest_size.w;
                int pix_mod_part = pix_count - (pix_count % 8);
                
                pt_dst = 0;
                pt_src = (i_top * src_size.w + i_left);
                for (int y = dest_size.h - 1; y >= 0; y -= 1)
                {
                    int x;
                    for (x = pix_count - 1; x >= pix_mod_part; x--)
                    {
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                    }
                    for (; x >= 0; x -= 8)
                    {
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                        output[pt_dst++] = input[pt_src];
                        pt_src += i_skip;
                    }
                    
                    pt_src += skip_src_y;
                }
                return;
            }
        }
    }
}
